/*
 *  Openmysee
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
				 
#include "util.h"
#define MAX_CLIENT	3072
#define MAX_CFG_LINE	1024
#define EscapedChar	"#["
static unsigned char hexchars[] = "0123456789ABCDEF";

struct hostent * init_sockaddr (struct sockaddr_in *name, char *host, unsigned int port)
{
	struct hostent *h;
	unsigned short sp = port;
	memset (name, 0, sizeof (*name));
	name->sin_family = PF_INET;
	name->sin_port = htons (sp);
	h = gethostbyname (host); /* The gethostbyname() function returns a structure of type hostent 
				     for the given host name. */
	if (h == (struct hostent *)0)
	{
		perror ("gethostbyname");
		return (struct hostent *)0;
	}
	name->sin_addr = *(struct in_addr *)h->h_addr;
	return h;
}
// create a connection, return a description
int my_connect (char *host, int port)
{
	struct sockaddr_in client;
	struct hostent *h;
	int connection = socket (PF_INET, SOCK_STREAM, 0);	

	// connection based
	if ((connection < 0) || ((h = init_sockaddr (&client, host, port)) == (struct hostent *)0))
	{
		perror ("socket||gethostbyname");
		return -1;
	}
	if (connect (connection, (struct sockaddr *) &client, sizeof (client)) < 0)
	{
		perror ("connect");
		return -1;
	}
	return connection;
}

int init_server (char *ip, unsigned short port)
{
	int flags;
	struct sockaddr_in server_address;
	int reuse_addr = 1;
	int keep_alive = 1;
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0)
	{
		perror("socket");
		return -1;
	}
	// 设置套接字属性
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
	setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive));

	flags = fcntl(sock, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(sock, F_SETFL, flags);

	memset((char *) &server_address, 0, sizeof(server_address));
	server_address.sin_port = htons(port);
	server_address.sin_family = AF_INET;

	//server_address.sin_addr.s_addr = htonl(INADDR_ANY);
	if (ip) inet_aton(ip, &server_address.sin_addr); /* inet_aton() converts
			the Internet host address ip from the standard numbers-and-dots notation into
			binary data and stores it in the server_address.sin_addr */
	else server_address.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(sock, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
	{
		perror("bind");
		close(sock);
		return -1;
	}
	listen(sock,MAX_CLIENT);
	return sock;
}

int read_config (char *fname, struct NamVal *para, int n)
{
	char *c, *pd;
	struct Array *parray;
	int array[MAX_ARRAY];
	int i, j, len, nlen;
	FILE *f = fopen (fname, "r");
	char buf[MAX_CFG_LINE];	// MAX_LINE = 1024
	if (!f)
	{
		return -1;
	}
	while (fgets (buf, MAX_CFG_LINE, f))
	{
		if (index(EscapedChar, buf[0]))
			continue;
		buf[MAX_CFG_LINE-1] = 0;
		len = strlen (buf);
		while (len > 0 && (buf[len] == 0 || buf[len] == '\n' || buf[len] == '\r' || buf[len] == ' '))
			len --;
		if (len == 0) continue;
		else buf[++len] = 0;
		c = index (buf, '=');
		*c = 0;
		nlen = c - buf;
		for (i=0; i<n; i++)
		{
			if (!strncasecmp (buf, para[i].name, nlen)) /* The strncasecmp() function 
			compares the two strings: the first nlen characters of buf and para[i].name, 
			ignoring the case of the characters. 
			It returns an integer less than, equal to, or greater than zero 
			if the first nlen characters of buf is found,
			respectively, to be less than, to match, or be greater than para[i].name. */
			break;
		}
		if (i < n)
			switch (para[i].type)
			{
				case 'd': // Int type 
					*(int *)(para[i].ptr) = atoi (c+1);
					break;
				case 'a':
					parray = (struct Array *)(para[i].ptr);
					pd = ++c;
					for (j=0; j<MAX_ARRAY; j++)
					{
						while ((*c != 0) && (*c < '0' || *c > '9')) c++;
						if (*c == 0) break;
						array[j] = strtol (c, &pd, 0);/* The strtol() function 
					converts the initial part of the string in c to
  					a long integer value according to the given 0, 
					which must be between 2 and 36 inclusive, or be the special value 0. */
						c = pd;
					}
					if (j > 0)
					{
						para[i].should_free = 1;
						parray->ptr = calloc (j, sizeof (int));
						memcpy (parray->ptr, array, sizeof (int)*j);
					}
					parray->size = j;
					break;
				case 'f': // float type 
					*(float *)(para[i].ptr) = atof (c+1);
					break;
				case 's': // Char string 
					if (len > nlen)
					{
						*(char **)(para[i].ptr) = (char *)calloc (len-nlen, 1);
						para[i].should_free = 1;
						memcpy (*(char **)(para[i].ptr), c+1, len-nlen);
					} else
						*(char **)(para[i].ptr) = 0;
					break;
				case 'b': // Buffer type 
					if (len > nlen)
						memcpy ((char *)(para[i].ptr), c+1, len-nlen);
					else
						*(char *)para[i].ptr = 0;
					break;
				default:
					break;
			}
		else
			continue;
	}
	fclose (f);
	return 0;
}

int free_config (struct NamVal *nv, int n)
{
	int i;
	for (i=0; i<n; i++)
	{
		switch (nv[i].type)
		{
			case 'a':
				if ((struct Array *)(nv[i].ptr) != NULL && nv[i].should_free == 1)
					free (((struct Array *)(nv[i].ptr))->ptr);
				break;
			case 's':
				if (*(char **)(nv[i].ptr) != NULL && nv[i].should_free == 1)
					free (*(char **)(nv[i].ptr));
				break;
			default:
				break;
		}
	}
	return 0;
}

int init_udp (char *ip, unsigned short port)
{
	int s = socket (PF_INET, SOCK_DGRAM, 0);
	struct sockaddr_in send;
	send.sin_port = htons(port);
	send.sin_family = AF_INET;
	if (ip) inet_aton(ip, &send.sin_addr);
	else send.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if(s < 0 || bind(s, (struct sockaddr*)&send, sizeof(send)) != 0)
	{
		perror("socket|bind");
		close(s);
		return -1;
	}
	return s;
}

int send_udp (int s, char *host, unsigned int port, char *buf, int len)
{
	struct sockaddr_in server;
	struct hostent *h;
	if ((h = init_sockaddr (&server, host, port)) == (struct hostent *)0)
	{
		perror ("socket||gethostbyname");
		return -1;
	}
	return sendto(s, buf, len, 0, (struct sockaddr*)&server,sizeof(server));
}

int sendMessage (int sock, char *ptr, struct sockaddr_in *dest)
{
	int len = *(int *)ptr;
	int socklen = sizeof (struct sockaddr_in);
	if (sendto (sock, ptr, len, 0, (struct sockaddr *)dest, socklen) != len)
		return -1;
	return len;
}

int connect_nonb(int sockfd, struct sockaddr_in *saptr, socklen_t salen)
{
	int		flags, n, error;

	flags = fcntl(sockfd, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(sockfd, F_SETFL, flags);

	error = 0;
	if ( (n = connect (sockfd, (struct sockaddr *) saptr, salen)) < 0)
	{
		if (errno != EINPROGRESS)
		{
			perror ("CP: Connect error:");
			return(-1);
		}
	}

	/* Do whatever we want while the connect is taking place. */
	return flags;
}

#ifdef HAVE_MYSQL
MYSQL *init_mysql (char *host, char *username, char *pass, char *db, char *sock)
{
	MYSQL *mysql = mysql_init (NULL);
	if (mysql == NULL)
	{
		PDEBUG ("mysql_init failed\n");
		return NULL;
	}
	if (mysql_real_connect (mysql, host, username, pass, db, 0, sock, 0) == NULL)
	{
		PDEBUG ("mysql_real_connect failed:%s.\n", mysql_error(mysql));
		return NULL;
	}
	return mysql;
}

int query_mysql (MYSQL *mysql, char *format, ...)
{
	char buffer[MAX_QUERY];
	int len;
	va_list ap;
	va_start (ap, format);
	len = vsnprintf (buffer, MAX_QUERY, format, ap);
	va_end (ap);
	if (len >= MAX_QUERY || len <= -1) return -1;
	if (mysql_query (mysql, buffer) != 0)
	{
		PDEBUG ("Error in query mysql database: %s.\n", mysql_error (mysql));
		return -1;
	}
	return 0;
}

MYSQL_RES *query_mysql_select (MYSQL *mysql, char *format, ...)
{
	char buffer[MAX_QUERY];
	int len;
	va_list ap;
	va_start (ap, format);
	len = vsnprintf (buffer, MAX_QUERY, format, ap);
	va_end (ap);
	if (len >= MAX_QUERY || len <= -1) return NULL;
	if (mysql_query (mysql, buffer) != 0)
	{
		PDEBUG ("Error in query mysql database: %s.\n", mysql_error (mysql));
		return NULL;
	}
	return mysql_store_result (mysql);
}

int authUser (int uid, char *md5, MYSQL *mysql, char *name)
{
	MYSQL_ROW row;
	MYSQL_RES *result_set;
	return 1;
	result_set = query_mysql_select (mysql, "select username from c_reg_users where userid = \"%d\" and password = \"%s\"", uid, md5);
	if (result_set && (row = mysql_fetch_row (result_set)) != NULL)
	{
		if (name) strcpy (name, row[0]);
		return 1;
	}
	if (result_set) mysql_free_result (result_set);
	return 0;
}
#endif

void php_url_encode(char *s, unsigned char *str)
{
	register int x, y;
	int len = strlen (s);
	
	for (x = 0, y = 0; len--; x++, y++) {
		str[y] = (unsigned char) s[x];

		if ((str[y] < '0' && str[y] != '-' && str[y] != '.' && str[y] != '/') ||
				(str[y] < 'A' && str[y] > '9') ||
				(str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
				(str[y] > 'z')) {
			str[y++] = '%';
			str[y++] = hexchars[(unsigned char) s[x] >> 4];
			str[y] = hexchars[(unsigned char) s[x] & 15];
		}
	}
	str[y] = '\0';
}


char *read_file (char *fname, int *dlen)
{
	register int32_t fd, cc;
	char *cp;
	struct stat buf;

	*dlen = 0;
	if ((fd = open (fname, O_RDONLY)) < 0)
		return NULL;

	if (fstat (fd, &buf) < 0)
		goto ERROR0;

	cp = (char*)malloc ((uint32_t) buf.st_size + 1);
	*dlen = buf.st_size;
	if ((cc = read (fd, cp, (int) buf.st_size)) < 0 || cc != buf.st_size)
		goto ERROR;
	cp[(int) buf.st_size] = '\0';
	close (fd);
	return (cp);
ERROR:
	free (cp);
ERROR0:
	close (fd);
	perror ("read_file:");
	return NULL;
}
